#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include <unistd.h>
#include <sys/wait.h>
#include "parser.h"
#include "process.h"

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>

#define _POSIX_SOURCE 1 /* POSIX compliant source */

int decoder(char *);
int execute(char **,int,listProcess);
void setNonCanonicalMode(void);
void setCanonicalMode(void);
void printSpaces(int);

struct termios oldtio;
int main(void){

	char *line = NULL;
	int command;
	char **commands;
	int n_commands;

	/* Coloca o terminal no modo não canonico, deixando todo o tratamento de entrada e saida para o bash */
	setNonCanonicalMode();

	listProcess process;
	listHistory hist;

	process = createListProcess();
	hist = loadHistory();

	system("clear");
	while(1) {
		/* Recebendo input do usuário */
		line = readLine(hist);

		commands = parser(line,&n_commands,' ');

		command = decoder(commands[0]);

		if(command == EXIT) {
			printf("\n\rSaindo...\n\r");
			setCanonicalMode();
			saveHistory(hist);
			exit(0);
 		} else if(command == CLEAR) {
			system("clear");
	        } else if(command == CHDIR) {
			prependNodeHistory(hist,line);
			if(chdir(commands[1]) == -1) {
				printf("\n\rDiretório não encontrado\n\r");
			}
		} else if(command == PRINT_HISTORY) {
			printf("\n\r");
			printHistory(hist);
		} else if(command == PROCESS_LIST) {
			printf("\n\r");
			printProcess(process);
		} else if(command == BLANK_LINE) {
			/*Não faz nada*/
			printf("\n\r");
		} else {
			printf("\n\r");
			if(execute(commands,n_commands,process)==-1) {
				printf("%s não é um programa ou comando reconhecido pelo bash\n\r",commands[0]);
				exit(1); /* Fecha a instância do fork que não conseguiu carregar um programa*/
			}
		}
		if(strcmp(line,"") != 0) {
			prependNodeHistory(hist,line);
		}
		free(line);
		line = NULL;
	}
	return 0;
}


void setNonCanonicalMode(void) {
	struct termios newtio;

	tcgetattr(0,&oldtio);
	bzero(&newtio,sizeof(newtio));

	newtio.c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD;
	newtio.c_iflag = IGNPAR;
        newtio.c_oflag = OPOST;

	/* set input mode (non-canonical, no echo,...) */
        newtio.c_lflag = 0;

        newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
        newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 chars received */

        tcflush(0, TCIFLUSH);
        tcsetattr(0,TCSANOW,&newtio);
}

void setCanonicalMode(void) {

        tcflush(0, TCIOFLUSH);
        tcsetattr(0,TCSANOW,&oldtio);
}

int decoder(char *string) {
	if(strcmp(string,"exit") == 0) {
		return EXIT;
	} else if(strcmp(string,"") == 0) {
		return BLANK_LINE;
	} else if(strncmp(string,"./",2) == 0){
		return EXTERNAL_COMMAND;
	} else if(strcmp(string,"clear") == 0) {
		return CLEAR;
 	} else if(strcmp(string,"cd") == 0) {
		return CHDIR;
	} else if(strcmp(string,"history") == 0) {
		return PRINT_HISTORY;
	} else if(strcmp(string,"pl") == 0) {
		return PROCESS_LIST;
	} else if(strcmp(string,"UP_HISTORY") == 0) {
		return UP_HISTORY;
	} else if(strcmp(string,"DOWN_HISTORY") == 0) {
		return DOWN_HISTORY;
	} else {
		return COMMAND_NOT_FOUND;
	}
}


int execute(char **commands, int n,listProcess p) {
	int pid;
	int wait_status;
	int i;
	int flag_bg = 0;

	if(strcmp(commands[n-1],"&") == 0)
		flag_bg = 1;

	pid = fork();

	if(pid < 0) {
		printf("Erro!\n");
		return -1;
	} else if(pid == 0) {
		/*child*/
		char **arg;

		insertNodeProcess(p,getpid(),FOREGROUND);
		arg = (char **)malloc((n+1)*sizeof(char *));
		for(i=0;i<n;i++) {
			arg[i] = commands[i];
		}
		arg[i] = NULL;

		if(flag_bg) {
			arg[i-1] = NULL;
		}

		setCanonicalMode();
		if(execvp(commands[0],arg)==-1) {
			return -1;
		}
	} else {
		/*father*/
		if(!flag_bg) {
			wait(&wait_status);
		}
		setNonCanonicalMode();
		return 1;
	}

	return 1;
}


